package cn.kivensoft.util;

import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;

/** 只读map，适用于小量数据字典，初始化设定值，后期无法修改
 * @author kiven lee
 * @version 1.0
 * @since 2020-03-02
 */
public class UnmodifiableMap implements Map<String, Object> {
	private boolean sorted;
	private final Object[] data;

	/** 创建键值对map
	 * @param args 键值对(key1, value1, key2, value2, ...)形式
	 */
	public static UnmodifiableMap of(Object... args) {
		checkArgs(args);
		return new UnmodifiableMap(false, args);
	}

	/** 创建键值对map
	 * @param map 源map, 该map内的所有键值都会被复制
	 * @param args 键值对(key1, value1, key2, value2, ...)形式
	 */
	public static UnmodifiableMap of(Map<String, Object> map, Object... args) {
		checkArgs(args);
		int pos = 0;
		Object[] objs = new Object[(map.size() << 1) + args.length];

		for (Map.Entry<String, Object> entry : map.entrySet()) {
			objs[pos++] = entry.getKey();
			objs[pos++] = entry.getValue();
		}

		if (args.length > 0)
			System.arraycopy(args, 0, objs, pos, args.length);

		return new UnmodifiableMap(false, objs);
	}

	/** 创建排序的键值对map
	 * @param args 键值对(key1, value1, key2, value2, ...)形式
	 */
	public static UnmodifiableMap sort(Object... args) {
		checkArgs(args);
		return new UnmodifiableMap(true, args);
	}

	/** 创建排序的键值对map
	 * @param map 源map, 该map内的所有键值都会被复制
	 * @param args 键值对(key1, value1, key2, value2, ...)形式
	 */
	public static UnmodifiableMap sort(Map<String, Object> map, Object... args) {
		UnmodifiableMap ret = of(map, args);
		ret.sorted = true;
		quickSortMap(ret.data);
		return ret;
	}

	private UnmodifiableMap(boolean sorted, Object[] data) {
		this.sorted = sorted;
		this.data = data;
		if (sorted) quickSortMap(data);
	}

	private int indexOfKey(Object key) {
		if (sorted)
			return binarySearchMap(data, (String) key);

		for (int i = 0, n = data.length - 1; i < n; i += 2)
			if (key.equals(data[i]))
				return i;
		return -1;
	}

	public int indexOf(String key) {
		int idx = indexOfKey(key);
		if (idx != -1) {
			int i = idx;
			while (i >= 0 && key.equals(data[i])) i -= 2;
			idx = (i + 2) >> 1;
		}
		return idx;
	}

	public String getKey(int index) {
		return (String) data[index << 1];
	}

	@SuppressWarnings("unchecked")
	public <T> T getValue(int index) {
		return (T) data[(index << 1) + 1];
	}

	@SuppressWarnings("unchecked")
	public final <T> T getx(String key) {
		return (T) get(key);
	}

	public final HashMap<String, Object> toHashMap() {
		HashMap<String, Object> map = new HashMap<>(data.length);
		for (int i = 0, n = data.length - 1; i < n; i += 2)
			map.put((String) data[i], data[i]); 
		return map;
	}

	/** 返回json格式化字符串表示 */
	public final String toJson() {
		return Fmt.toJson(this);
	}

	@Override
	public int size() {
		return data.length >> 1;
	}

	@Override
	public boolean isEmpty() {
		return data.length == 0;
	}

	@Override
	public boolean containsKey(Object key) {
		return indexOfKey(key) != -1;
	}

	@Override
	public boolean containsValue(Object value) {
		for (int i = 1, n = data.length; i < n; i += 2)
			if (value.equals(data[i]))
				return true;
		return false;
	}

	@Override
	public Object get(Object key) {
		int i = indexOfKey(key);
		return (i == -1) ? null : data[i + 1];
	}

	@Deprecated
	@Override
	public Object put(String key, Object value) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public Object remove(Object key) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public void putAll(Map<? extends String, ?> m) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public void clear() {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public Object merge(String key, Object value, BiFunction<? super Object, ? super Object, ?> func) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public Object putIfAbsent(String key, Object value) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public boolean remove(Object key, Object value) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public boolean replace(String key, Object oldValue, Object newValue) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public Object replace(String key, Object value) {
		throw new UnsupportedOperationException();
	}

	@Deprecated
	@Override
	public void replaceAll(BiFunction<? super String, ? super Object, ?> function) {
		throw new UnsupportedOperationException();
	}

	private static void checkArgs(Object... args) {
		int len = args.length;
		if ((len & 1) != 0)
			throw new IllegalArgumentException("args count must even numbers");
		for (int i = 0; i < len; i += 2)
			if (args[i].getClass() != String.class)
				throw new IllegalArgumentException(Fmt.fmt("args[{}] must be String", i));
	}

    @Override
    public void forEach(BiConsumer<? super String, ? super Object> action) {
    	for (int i = 0, n = data.length - 1; i < n; i += 2)
    		action.accept((String) data[i], data[i + 1]);
    }

	@Override
	public Set<String> keySet() {
		return new KeySet();
	}

	@Override
	public Collection<Object> values() {
		return new Values();
	}

	@Override
	public Set<Entry<String, Object>> entrySet() {
		return new EntrySet();
	}

	final static class Node implements Map.Entry<String, Object> {
		String key;
		Object value;
		public String getKey() { return key; }
		public Object getValue() { return value; }
		public Object setValue(Object value) { throw new UnsupportedOperationException(); }
        public int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); }
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
				return Objects.equals(key, e.getKey())
						&& Objects.equals(value, e.getValue());
            }
            return false;
        }
	}

    abstract class BaseIter {
		int pos;
		public final boolean hasNext() { return pos + 1 < data.length; }
    }

    final class EntryIter extends BaseIter implements Iterator<Map.Entry<String, Object>> {
		Node node = new Node();

		public Map.Entry<String, Object> next() {
			node.key = (String) data[pos];
			node.value = data[pos + 1];
			pos += 2;
			return node;
		}
    }

	final class EntrySet extends AbstractSet<Map.Entry<String, Object>> {
		public int size() { return UnmodifiableMap.this.size(); }
		public void clear() { UnmodifiableMap.this.clear(); }
		public boolean contains(Object o) { return UnmodifiableMap.this.containsKey(o); }
		@Deprecated
		public boolean remove(Object o) { return UnmodifiableMap.this.remove(o) != null; }

		public Iterator<Map.Entry<String, Object>> iterator() {
			return new EntryIter();
		}

		public void forEach(Consumer<? super Map.Entry<String, Object>> action) {
			Node node = new Node();
			for (int i = 0, n = data.length - 1; i < n; i += 2) {
				node.key = (String) data[i];
				node.value = data[i + 1];
				action.accept(node);
			}
		}
	}

    final class KeyIter extends BaseIter implements Iterator<String> {
		public String next() {
			pos += 2;
			return (String) data[pos - 2];
		}
    }

    final class ValueIter extends BaseIter implements Iterator<Object> {
		public Object next() {
			pos += 2;
			return data[pos - 1];
		}
    }

	final class KeySet extends AbstractSet<String> {
		public int size() { return UnmodifiableMap.this.size(); }
		public void clear() { UnmodifiableMap.this.clear(); }
		public boolean contains(Object o) { return containsKey(o); }
		public boolean remove(Object key) { return UnmodifiableMap.this.remove(key) != null; }
		public Iterator<String> iterator() { return new KeyIter(); }

		public void forEach(Consumer<? super String> action) {
			for (int i = 0, n = data.length - 1; i < n; i += 2)
				action.accept((String) data[i]);
		}
	}

	final class Values extends AbstractCollection<Object> {
		public int size() { return UnmodifiableMap.this.size(); }
		public void clear() { UnmodifiableMap.this.clear(); }
		public boolean contains(Object o) { return containsValue(o); }
		public Iterator<Object> iterator() { return new ValueIter(); }

		public void forEach(Consumer<? super Object> action) {
			for (int i = 1, n = data.length; i < n; ++i)
				action.accept(data[i]);
		}
	}

	/** 快速排序算法实现
	 * @param array 要进行排序的数组
	 */
	public static <T extends Comparable<T>> void quickSort(T[] array) {
		quickSort(array, 0, array.length - 1);
	}

	// 递归快速排序
	private static <T extends Comparable<T>> void quickSort(T[] array, int low, int high) {
		if (low < high) {
			int l = low, h = high;
			// temp就是基准位
			T temp = array[low];

			while (l < h) {
				// 先看右边，依次往左递减
				while (temp.compareTo(array[h]) <= 0 && l < h) h--;
				// 再看左边，依次往右递增
				while (temp.compareTo(array[l]) >= 0 && l < h) l++;
				// 如果满足条件则交换
				if (l < h) {
					T t = array[h];
					array[h] = array[l];
					array[l] = t;
				}
			}

			// 最后将基准为与i和j相等位置的数字交换
			array[low] = array[l];
			array[l] = temp;
			// 递归调用左半数组
			quickSort(array, low, h - 1);
			// 递归调用右半数组
			quickSort(array, h + 1, high);
		}
	}

	/** 二分法查找，适用于已经从小到大排序好的素组
	 * @param array 要查找的数组
	 * @param key 查找的键
	 * @return 找到值的索引位置，-1为未找到
	 */
	public static <T extends Comparable<T>> int binarySearch(T[] array, T key) {
		int low = 0, high = array.length - 1, middle;

		if (key.compareTo(array[low]) < 0 || key.compareTo(array[high]) > 0)
			return -1;

		while (low <= high) {
			middle = (low + high) / 2;
			int r = key.compareTo(array[middle]);
			if (r < 0) high = middle - 1;
			else if (r > 0) low = middle + 1;
			else return middle;
		}

		return -1;
	}

	// 快速排序，字典专用
	private static void quickSortMap(Object[] array) {
		quickSortMap(array, 0, array.length - 1);
	}

	// 递归快速排序
	private static void quickSortMap(Object[] array, int low, int high) {
		if (low < high - 1) {
			int l = low, h = high - 1;
			// temp就是基准位
			String temp = (String) array[low];
			Object temp_value;

			while (l < h) {
				// 先看右边，依次往左递减
				while (temp.compareTo((String) array[h]) <= 0 && l < h)
					h -= 2;
				// 再看左边，依次往右递增
				while (temp.compareTo((String) array[l]) >= 0 && l < h)
					l += 2;
				// 如果满足条件则交换
				if (l < h) {
					String t = (String) array[h];
					array[h] = array[l];
					array[l] = t;
					temp_value = array[h + 1];
					array[h + 1] = array[l + 1];
					array[l + 1] = temp_value;
				}
			}

			// 最后将基准为与i和j相等位置的数字交换
			array[low] = array[l];
			array[l] = temp;
			temp_value = array[low + 1];
			array[low + 1] = array[l + 1];
			array[l + 1] = temp_value;
			// 递归调用左半数组
			quickSortMap(array, low, h - 1);
			// 递归调用右半数组
			quickSortMap(array, h + 2, high);
		}
	}

	// 二分法查找
	private static int binarySearchMap(Object[] array, String key) {
		int low = 0, high = array.length - 2, middle;

		if (key.compareTo((String) array[low]) < 0 || key.compareTo((String) array[high]) > 0)
			return -1;

		while (low <= high) {
			middle = (low + high) / 2;
			if ((middle & 1) != 0) ++middle;
			int r = key.compareTo((String) array[middle]);
			if (r < 0) high = middle - 2;
			else if (r > 0) low = middle + 2;
			else return middle;
		}

		return -1;
	}

}
